/**
 * 
 */
package com.gragra.sampling.binary;
import java.util.Arrays;
import java.util.Collection;
import com.gragra.benchmark.CountingAssigner;
import com.gragra.sampling.PopulationSamplingStructure;
/**
 * @author Christoph Teichmann
 * 20.09.2013
 */
public class FixedConstAssigner extends UniformAssigner implements
		ChartAbleAssigner, CountingAssigner
{
	/**
	 * 
	 */
	private final int depth;
	/* (non-Javadoc)
	 * @see com.gragra.sampling.binary.UniformAssigner#makeLogProb(com.gragra.sampling.PopulationSamplingStructure, int)
	 */
	@Override
	public double makeLogProb(PopulationSamplingStructure pss, int thread)
	{
		double val = Double.NEGATIVE_INFINITY;
		BinaryStructure bs = (BinaryStructure) pss;
		this.functs += bs.getLength()-1;
		for(boolean[][] a : this.consts)
		{val = Math.max(makeSum(pss,thread,a),val);}
		return val;
	}
	/**
	 * 
	 * @param pss
	 * @param thread
	 * @param a
	 * @return
	 */
	private double makeSum(PopulationSamplingStructure pss, int thread,
			boolean[][] a)
	{
		double sum = 0.0;
		BinaryStructure bs = (BinaryStructure) pss;
		for(int node=bs.getLength();node<=bs.getRootPosition();++node)
		{sum += makeSingleNode(node,bs,thread,a);}
		return sum;
	}
	/**
	 * 
	 * @param node
	 * @param bs
	 * @param thread
	 * @param a 
	 * @return
	 */
	private double makeSingleNode(int node, BinaryStructure bs, int thread, boolean[][] a)
	{
		boolean found = true;
		for(int i=0;i<this.depth;++i)
		{
			int from = bs.getFrom(node, thread);
			int to = bs.getTo(node, thread);
			found &= a[from][to];
			if(!found)
			{break;}
			node = node == bs.getRootPosition() ? node : bs.getAbove(node, thread);
			if(node == bs.getRootPosition())
			{break;}
		}
		return found ? this.weight : 0.0;
	}
	/* (non-Javadoc)
	 * @see com.gragra.sampling.binary.UniformAssigner#getLocalLogLike(com.gragra.sampling.PopulationSamplingStructure, int)
	 */
	@Override
	public double getLocalLogLike(PopulationSamplingStructure pss, int thread)
	{return makeLogProb(pss, thread);}
	/**
	 * 
	 */
	private final double weight;
	/**
	 * 
	 */
	private final boolean[][][] consts;
	/**
	 * @param pop
	 */
	public FixedConstAssigner(int pop, boolean[][][] consts, double weight, int depth)
	{
		super(pop);
		this.consts = consts;
		this.depth = depth;
		this.weight = weight;
	}
	/**
	 * @param pop
	 */
	public FixedConstAssigner(int pop, Collection<String> consts, double weight, int depth)
	{
		super(pop);
		this.consts = makeConsts(consts);
		this.depth = depth;
		this.weight = weight;
	}
	/**
	 * 
	 * @param consts2
	 * @return
	 */
	private boolean[][][] makeConsts(Collection<String> cs)
	{
		int max = makeMax(cs)+1;
		boolean[][][] arr = new boolean[cs.size()][max][max];
		falsify(arr);
		int a = 0;
		for(String s : cs)
		{
			String[] parts = s.split("//")[0].trim().split("\\s+");
			for(String p : parts)
			{
				String[] l = p.split(";");
				arr[a][Integer.parseInt(l[0])][Integer.parseInt(l[1])] = true;
			}
			++a;
		}
		return arr;
	}
	/**
	 * 
	 * @param arr
	 */
	private void falsify(boolean[][][] arr)
	{
		for(boolean[][] a : arr)
		{
			for(boolean[] b : a)
			{Arrays.fill(b, false);}
		}
	}
	/**
	 * 
	 * @param cs
	 * @return
	 */
	private int makeMax(Collection<String> cs)
	{
		int max = 0;
		for(String s : cs)
		{
			String[] parts = s.split("\\s+");
			for(String p : parts)
			{
				String[] l = p.split(";");
				max = Math.max(Integer.parseInt(l[0]), max);
				max = Math.max(Integer.parseInt(l[1]), max);
			}
		}
		return max;
	}
	/* (non-Javadoc)
	 * @see com.gragra.sampling.binary.UniformAssigner#getTransitionLogLike(com.gragra.sampling.binary.BinaryChart, int, int, int, int)
	 */
	@Override
	public double getTransitionLogLike(BinaryChart binaryChart, int thread,
			int start, int middle, int end)
	{
		++auxs;
		double max = 0.0;
		for(boolean[][] b : this.consts)
		{
			double sum = 0.0;
			if(this.depth < 2)
			{
				sum += b[start][end] ? this.weight : 0.0;
			}
			else
			{
				if(b[start][end])
				{
					sum += b[start][middle] ? this.weight : 0.0;
					sum += b[middle][end] ? this.weight : 0.0;
				}
			}
			max = Math.max(sum, max);
		}
		return max;
	}
	/**
	 * 
	 */
	protected int auxs = 0;
	/**
	 * 
	 */
	protected int functs = 0;
	@Override
	public double getNumberOfEvaluations()
	{return functs;}
	@Override
	public double getNumberOfAuxEvals()
	{return auxs;}
}